package crawler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;

/**
 * The virtual browser makes it possible to behave as though one
 * were naturally browsing the interwebs.  It combines the power
 * of two open-source libraries, Apache's HttpClient and the Java
 * HtmlParser.
 * 
 * @author Kevin Dolan
 */
public class VirtualBrowser {

	private HttpClient client;
	public VirtualBrowser() {
        // Create and initialize scheme registry 
        SchemeRegistry schemeRegistry = (new DefaultHttpClient()).getConnectionManager().getSchemeRegistry();
        HttpParams params = (new DefaultHttpClient()).getParams();
        ConnManagerParams.setMaxTotalConnections(params, 100);
        
        // Create an HttpClient with the ThreadSafeClientConnManager.
        ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
        client = new DefaultHttpClient(cm, params);
	}
	
	/**
	 * Convert a map to a list of NameValuePairs
	 * @param parameters the map to convert
	 * @return			 the name value pair list
	 */
	private List<NameValuePair> toPairs(Map<String, String> parameters) {
		Set<Entry<String, String>> set = parameters.entrySet();
		ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
		for(Entry<String, String> entry : set)
			pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		return pairs;
	}
	
	/**
	 * Perform a request
	 * @param post		 true to send this via POST, false for GET
	 * @param url 		 the url to access
	 * @param referrer 	 the source url, null if undefined
	 * @param parameters the map of parameters to send, null if none
	 * @return			 the body of the page, or null if an error occurred
	 */
	public String request(boolean post, String url, String referrer, Map<String, String> parameters) {
		try {
			HttpRequestBase method;
			if(post)
				method = new HttpPost(url);
			else
				method = new HttpGet(url);
			
			if(referrer != null) 
				method.setHeader("referer", referrer);
			
			if(parameters != null && parameters.size() > 0) {
				if(post)
					((HttpPost)method).setEntity(new UrlEncodedFormEntity(toPairs(parameters), HTTP.UTF_8));
				else {
					String params = URLEncodedUtils.format(toPairs(parameters), "UTF-8");
					if(url.indexOf("?") == -1)
						url += "?";
					else
						url += "&";
					url += params;
				}
			}
			
			ResponseHandler<String> responseHandler = new BasicResponseHandler();
			String responseBody = client.execute(method, responseHandler);
			
			return responseBody;
		} catch (Exception e) {
			System.err.println(e.getClass());
			return null;
		}
	}
	
	/**
	 * Close the http connection manager to clear up resources
	 */
	public void close() {
		client.getConnectionManager().shutdown();
	}
	
}
